library(tidyverse)
library(sn)
library(knitr)
library(altair)
library(reticulate)
alt$data_transformers$disable_max_rows()
# Set basic theme for plots
theme_hcb <- theme_classic() +
theme(panel.grid.major.y=element_line(colour = 'lightgrey')
)
theme_set(theme_hcb)
The best way to calculate the rate of death from an illness is to look at the outcome for each confirmed case but data with this level of detail isn’t always released publicly. NSW Health publishes daily cases and deaths for COVID as well as some summary statistics in a weekly surveillance report. The death rate can be estimated using the daily figures but it is necessary to account for the lag time between testing positive and passing away. This is pretty straightforward, but what should the lag be?
The weekly surveillance reports published by NSW Health3 state a median lag of 11 days from symptom onset to death. This has been consistent throughout the second half of 2021 where Delta was the dominant variant in NSW. It is also possible to calculate the observed lag by comparing case numbers and death numbers. This method shows a lag of 20 days2. This is a big difference! The observed lag is almost double the median.
These numbers point to a flat, heavily skewed distribution1. It makes sense that it could be approximated with a series of Poisson distributions, but without more information it’s just guesswork. It’s easier (and enough to show the effects) to define a distribution that has some of the observed properties and see what happens when it is applied to real world data. This should help explain the difference in the median lag and the observed lag and inform the choice of lag time.
# Skewed distribution to approximate observed data
set.seed(55555)
# Set shape parameters
om=15
al=6
del=1
sn = rsn(n=10000, xi=del, omega=om, alpha=al)
sn = sn[sn>=0]
A skewed normal distribution was used to approximate the data. The median was set to match the NSW Health figure of 11 days, with a shape resembling those published by UK Health1. NB: This distribution is for the original strain.
# Plot approximation distribution
SN=data.frame(sn)
ggplot(data = SN, mapping = aes(x=sn)) +
geom_histogram(
color='white',
fill='skyblue',
bins=50
) +
labs(
x='Days after testing positive',
y='Count',
title='Approximate distribution of days until death'
)

Summary statistics for the model distribution:
# Summary stats for generated data
kable(as.matrix(t(summary(sn))))
| 0.0060008 |
6.236993 |
11.4796 |
13.28834 |
18.57076 |
65.27701 |
This corresponds to the distribution of deaths from one day of infections.
Using the case number data from NSW Health (left graph), it should be possible to re-create the general shape of the death data. For each day a random sample is taken from the model distribution, essentially simulating when the deaths would occur. These are first laid over each other (top right graph) and then stacked on top of each other (bottom right graph).
The purpose here is to show how the difference in lag occurs, so the overall shapes are more important than the number totals. Due to the relatively small number of cases each day, rather than take a percentage of cases to reflect the death rate no scaling has been done.
Selecting a day on the left graph shows where the deaths from that day would likely occur. The long right tail of the model distribution pushes the peak of the stacked histogram to the right.
# Read in case data and select NSW
cases = read.csv('cases_daily_state_NET.csv')
cases <- subset(cases, select=c(ï..Date,NSW))
# Convert date format and select time period of interest
cases <- rename(cases, Date=ï..Date)
cases$Date <- as.Date(cases$Date, '%d/%m/%y')
cases <- cases %>% filter(Date>'2021-06-15' & Date<'2021-10-31')
# Convert count format
cases$NSW <- as.numeric(gsub(",", "", cases$NSW))
# Make data frame with distributions
DS = data.frame(pos=integer(),day=character())
for(i in 1:length(cases$NSW))
{
# generate distribution for that days positive tests + num days since start
pos_tests = cases$NSW[i]
if (pos_tests > 0) {
d_dist <- rsn(n=pos_tests, xi=del, omega=om, alpha=al)
d_dist <- d_dist[d_dist>=0]+i
d_dist <- data.frame(d_dist, i) %>% rename(deaths=d_dist,day=i)
DS <- rbind(DS, d_dist)
}
}
# Convert day of infection and death to date - Altair needs timestamp
# Set start date
start <- as.POSIXct(as.Date('2021-06-15'))
# Define one day
plus1 <- as.POSIXct(as.Date('2021-06-16'))
one_day <- plus1-start
# Add to DS frame
DS$Date <- start + DS$day*one_day
DS$d_date <- start + floor(DS$deaths)*one_day
DS <- subset(DS, select = c(deaths, Date))
# Set number of bins for histograms
num_bins <- ceiling(max(DS$deaths)-min(DS$deaths))
# Extra entry - hack to extend x-axis for case histogram
# Altair doesn't do this nicely with datetimes
# NB there are 187 days from 16/6 - 20/12
#end <- as.POSIXct(as.Date('2021-12-20'))
#row <- data.frame(179,end)
#names(row) <- c("deaths", "Date")
#DS <- rbind(DS, row)
### Build interactive charts
# Define interactive plot selector
selector <- alt$selection_single(empty = "all", fields = list("Date"))
# Histogram of cases with line and date of max cases
h_case <-
alt$Chart(DS)$
mark_bar(size=2)$
encode(
x = alt$X(
"Date:T",
axis=alt$Axis(
title='Date',
grid=F
),
timeUnit="monthdate"
),
y = "count(Date):Q",
color = alt$condition(
selector,
"Date:T",
legend=NULL,
scale=alt$Scale(scheme=alt$SchemeParams(name='viridis', extent=c(-0.5,1.5))),
alt$value("lightgray")
)
)$
properties(
selection = selector,
title='Daily cases - NSW'
)
# Create dataframe with mode value for line
m_case <- as.POSIXct(as.Date('2021-09-11'))
# Put vertical line showing mode on graph
l_case <-
alt$Chart(data.frame(m_case))$
mark_rule(
color='darkgrey',
strokeDash=array(1,1)
)$
encode(x='monthdate(m_case):T')
# Create text dataframe
td_case <- '11-Sep'
t_case <-
alt$Chart(data.frame(td_case))$
mark_text(
dx=52,
dy=-110
)$
encode(text='td_case:N')$
properties(width = 350, height = 200)
# Combine layers
d_case <- h_case + l_case + t_case
### Distribution plots
# Overlaid histograms
h_over <-
alt$Chart(DS)$
mark_area(interpolate="step")$
encode(
x = alt$X(
"deaths",
bin=alt$Bin(maxbins=num_bins),
scale = alt$Scale(domain = c(0, 187)),
axis=alt$Axis(title='Days since 16th June 2021')
),
y = alt$Y(
"count():Q",
stack=F,
scale = alt$Scale(domain = c(0, 100))
),
color = alt$Color(
"Date:T",
legend=NULL
),
opacity=alt$condition(
selector,
alt$value(1),
alt$value(0.05)
)
)$
add_selection(selector)$
properties(
selection = selector,
title='Date of death histograms - overlaid'
)
# Create dataframe with mode value for line
m_over <- 95
l_over <-
alt$Chart(data.frame(m_over))$
mark_rule(
color='darkgrey',
strokeDash=array(1,1)
)$
encode(x='m_over:Q')
# Create text dataframe
td_over <- '19-Sep'
t_over <-
alt$Chart(data.frame(td_over))$
mark_text(
dx=-11,
dy=-110
)$
encode(text='td_over:N')$
properties(width = 350, height = 200)
# Combine layers
d_over <- h_over + l_over + t_over
# Stacked histograms
h_stak <-
alt$Chart(DS)$
mark_area(interpolate="step")$
encode(
x = alt$X(
"deaths",
bin=alt$Bin(maxbins=num_bins),
scale = alt$Scale(domain = c(0, 187)),
axis=alt$Axis(title='Days since 16th June 2021')
),
y = alt$Y(
"count()",
stack=T,
scale = alt$Scale(domain = c(0, 1250))
),
color = alt$Color(
"Date:T",
legend=NULL
),
opacity=alt$condition(
selector,
alt$value(1),
alt$value(0.3)
)
)$
add_selection(selector)$
properties(
selection = selector,
title='Date of death histograms - stacked'
)
# Create dataframe with mode value for line
m_stak <- 100
l_stak <-
alt$Chart(data.frame(m_stak))$
mark_rule(
color='darkgrey',
strokeDash=array(1,1)
)$
encode(x='m_stak:Q')
# Create text dataframe
td_stak <- '24-Sep'
t_stak <-
alt$Chart(data.frame(td_stak))$
mark_text(
dx=-2,
dy=-110
)$
encode(text='td_stak:N')$
properties(width = 350, height = 200)
d_stak <- h_stak + l_stak + t_stak
# Combine all graphs
final <- d_case | (d_over & d_stak)
final$configure_title(anchor='start')
NA
When the distributions are overlaid the highest point is at 19th September - an 8 day lag. When they stacked the peak is the 24th September - a 13 day lag. Clearly the model distribution here is not quite right, but it serves to demonstrate how all the distributions relate to each other. It is also clearer now that using the median as the lag will produce a less accurate estimation of the death rate when looking at the deaths reported each day. In practice it will under-estimate as cases increase and over-estimate as cases decrease. Aligning the peaks of the case numbers and the daily deaths will give the best approximation.
LS0tDQp0aXRsZTogIkNPVklEIGRlYXRoIGxhZyBkaXNjdXNzaW9uIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICBkZl9wcmludDoga2FibGUNCi0tLQ0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc24pDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShhbHRhaXIpDQpsaWJyYXJ5KHJldGljdWxhdGUpDQphbHQkZGF0YV90cmFuc2Zvcm1lcnMkZGlzYWJsZV9tYXhfcm93cygpDQoNCiMgU2V0IGJhc2ljIHRoZW1lIGZvciBwbG90cw0KdGhlbWVfaGNiIDwtIHRoZW1lX2NsYXNzaWMoKSArDQogICAgICAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfbGluZShjb2xvdXIgPSAnbGlnaHRncmV5JykNCiAgICAgICAgICAgICAgKQ0KdGhlbWVfc2V0KHRoZW1lX2hjYikNCmBgYA0KVGhlIGJlc3Qgd2F5IHRvIGNhbGN1bGF0ZSB0aGUgcmF0ZSBvZiBkZWF0aCBmcm9tIGFuIGlsbG5lc3MgaXMgdG8gbG9vayBhdCB0aGUgb3V0Y29tZSBmb3IgZWFjaCBjb25maXJtZWQgY2FzZSBidXQgZGF0YSB3aXRoIHRoaXMgbGV2ZWwgb2YgZGV0YWlsIGlzbid0IGFsd2F5cyByZWxlYXNlZCBwdWJsaWNseS4gTlNXIEhlYWx0aCBwdWJsaXNoZXMgZGFpbHkgY2FzZXMgYW5kIGRlYXRocyBmb3IgQ09WSUQgYXMgd2VsbCBhcyBzb21lIHN1bW1hcnkgc3RhdGlzdGljcyBpbiBhIHdlZWtseSBzdXJ2ZWlsbGFuY2UgcmVwb3J0LiBUaGUgZGVhdGggcmF0ZSBjYW4gYmUgZXN0aW1hdGVkIHVzaW5nIHRoZSBkYWlseSBmaWd1cmVzIGJ1dCBpdCBpcyBuZWNlc3NhcnkgdG8gYWNjb3VudCBmb3IgdGhlIGxhZyB0aW1lIGJldHdlZW4gdGVzdGluZyBwb3NpdGl2ZSBhbmQgcGFzc2luZyBhd2F5LiBUaGlzIGlzIHByZXR0eSBzdHJhaWdodGZvcndhcmQsIGJ1dCB3aGF0IHNob3VsZCB0aGUgbGFnIGJlPyANCg0KVGhlIHdlZWtseSBzdXJ2ZWlsbGFuY2UgcmVwb3J0cyBwdWJsaXNoZWQgYnkgTlNXIEhlYWx0aDxzdXA+Mzwvc3VwPiBzdGF0ZSBhIG1lZGlhbiBsYWcgb2YgMTEgZGF5cyBmcm9tIHN5bXB0b20gb25zZXQgdG8gZGVhdGguIFRoaXMgaGFzIGJlZW4gY29uc2lzdGVudCB0aHJvdWdob3V0IHRoZSBzZWNvbmQgaGFsZiBvZiAyMDIxIHdoZXJlIERlbHRhIHdhcyB0aGUgZG9taW5hbnQgdmFyaWFudCBpbiBOU1cuIEl0IGlzIGFsc28gcG9zc2libGUgdG8gY2FsY3VsYXRlIHRoZSBvYnNlcnZlZCBsYWcgYnkgY29tcGFyaW5nIGNhc2UgbnVtYmVycyBhbmQgZGVhdGggbnVtYmVycy4gVGhpcyBtZXRob2Qgc2hvd3MgYSBsYWcgb2YgMjAgZGF5czxzdXA+Mjwvc3VwPi4gVGhpcyBpcyBhIGJpZyBkaWZmZXJlbmNlISBUaGUgb2JzZXJ2ZWQgbGFnIGlzIGFsbW9zdCBkb3VibGUgdGhlIG1lZGlhbi4NCg0KVGhlc2UgbnVtYmVycyBwb2ludCB0byBhIGZsYXQsIGhlYXZpbHkgc2tld2VkIGRpc3RyaWJ1dGlvbjxzdXA+MTwvc3VwPi4gSXQgbWFrZXMgc2Vuc2UgdGhhdCBpdCBjb3VsZCBiZSBhcHByb3hpbWF0ZWQgd2l0aCBhIHNlcmllcyBvZiBQb2lzc29uIGRpc3RyaWJ1dGlvbnMsIGJ1dCB3aXRob3V0IG1vcmUgaW5mb3JtYXRpb24gaXQncyBqdXN0IGd1ZXNzd29yay4gSXQncyBlYXNpZXIgKGFuZCBlbm91Z2ggdG8gc2hvdyB0aGUgZWZmZWN0cykgdG8gZGVmaW5lIGEgZGlzdHJpYnV0aW9uIHRoYXQgaGFzIHNvbWUgb2YgdGhlIG9ic2VydmVkIHByb3BlcnRpZXMgYW5kIHNlZSB3aGF0IGhhcHBlbnMgd2hlbiBpdCBpcyBhcHBsaWVkIHRvIHJlYWwgd29ybGQgZGF0YS4gVGhpcyBzaG91bGQgaGVscCBleHBsYWluIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBtZWRpYW4gbGFnIGFuZCB0aGUgb2JzZXJ2ZWQgbGFnIGFuZCBpbmZvcm0gdGhlIGNob2ljZSBvZiBsYWcgdGltZS4NCg0KYGBge3J9DQojIFNrZXdlZCBkaXN0cmlidXRpb24gdG8gYXBwcm94aW1hdGUgb2JzZXJ2ZWQgZGF0YQ0Kc2V0LnNlZWQoNTU1NTUpDQojIFNldCBzaGFwZSBwYXJhbWV0ZXJzDQpvbT0xNQ0KYWw9Ng0KZGVsPTENCnNuID0gcnNuKG49MTAwMDAsIHhpPWRlbCwgb21lZ2E9b20sIGFscGhhPWFsKQ0Kc24gPSBzbltzbj49MF0NCmBgYA0KQSBza2V3ZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3YXMgdXNlZCB0byBhcHByb3hpbWF0ZSB0aGUgZGF0YS4gVGhlIG1lZGlhbiB3YXMgc2V0IHRvIG1hdGNoIHRoZSBOU1cgSGVhbHRoIGZpZ3VyZSBvZiAxMSBkYXlzLCB3aXRoIGEgc2hhcGUgcmVzZW1ibGluZyB0aG9zZSBwdWJsaXNoZWQgYnkgVUsgSGVhbHRoPHN1cD4xPC9zdXA+LiBOQjogVGhpcyBkaXN0cmlidXRpb24gaXMgZm9yIHRoZSBvcmlnaW5hbCBzdHJhaW4uDQpgYGB7ciBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTJ9DQojIFBsb3QgYXBwcm94aW1hdGlvbiBkaXN0cmlidXRpb24NClNOPWRhdGEuZnJhbWUoc24pDQpnZ3Bsb3QoZGF0YSA9IFNOLCBtYXBwaW5nID0gYWVzKHg9c24pKSArDQogIGdlb21faGlzdG9ncmFtKA0KICAgIGNvbG9yPSd3aGl0ZScsDQogICAgZmlsbD0nc2t5Ymx1ZScsDQogICAgYmlucz01MA0KICApICsNCiAgbGFicygNCiAgICB4PSdEYXlzIGFmdGVyIHRlc3RpbmcgcG9zaXRpdmUnLCANCiAgICB5PSdDb3VudCcsIA0KICAgIHRpdGxlPSdBcHByb3hpbWF0ZSBkaXN0cmlidXRpb24gb2YgZGF5cyB1bnRpbCBkZWF0aCcNCiAgKQ0KYGBgDQpTdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBtb2RlbCBkaXN0cmlidXRpb246DQpgYGB7ciByZXN1bHRzPSdhc2lzJ30NCiMgU3VtbWFyeSBzdGF0cyBmb3IgZ2VuZXJhdGVkIGRhdGENCmthYmxlKGFzLm1hdHJpeCh0KHN1bW1hcnkoc24pKSkpDQpgYGANCg0KVGhpcyBjb3JyZXNwb25kcyB0byB0aGUgZGlzdHJpYnV0aW9uIG9mIGRlYXRocyBmcm9tIG9uZSBkYXkgb2YgaW5mZWN0aW9ucy4gDQoNClVzaW5nIHRoZSBjYXNlIG51bWJlciBkYXRhIGZyb20gTlNXIEhlYWx0aCAobGVmdCBncmFwaCksIGl0IHNob3VsZCBiZSBwb3NzaWJsZSB0byByZS1jcmVhdGUgdGhlIGdlbmVyYWwgc2hhcGUgb2YgdGhlIGRlYXRoIGRhdGEuIEZvciBlYWNoIGRheSBhIHJhbmRvbSBzYW1wbGUgaXMgdGFrZW4gZnJvbSB0aGUgbW9kZWwgZGlzdHJpYnV0aW9uLCBlc3NlbnRpYWxseSBzaW11bGF0aW5nIHdoZW4gdGhlIGRlYXRocyB3b3VsZCBvY2N1ci4gVGhlc2UgYXJlIGZpcnN0IGxhaWQgb3ZlciBlYWNoIG90aGVyICh0b3AgcmlnaHQgZ3JhcGgpIGFuZCB0aGVuIHN0YWNrZWQgb24gdG9wIG9mIGVhY2ggb3RoZXIgKGJvdHRvbSByaWdodCBncmFwaCkuIA0KDQpUaGUgcHVycG9zZSBoZXJlIGlzIHRvIHNob3cgaG93IHRoZSBkaWZmZXJlbmNlIGluIGxhZyBvY2N1cnMsIHNvIHRoZSBvdmVyYWxsIHNoYXBlcyBhcmUgbW9yZSBpbXBvcnRhbnQgdGhhbiB0aGUgbnVtYmVyIHRvdGFscy4gRHVlIHRvIHRoZSByZWxhdGl2ZWx5IHNtYWxsIG51bWJlciBvZiBjYXNlcyBlYWNoIGRheSwgcmF0aGVyIHRoYW4gdGFrZSBhIHBlcmNlbnRhZ2Ugb2YgY2FzZXMgdG8gcmVmbGVjdCB0aGUgZGVhdGggcmF0ZSBubyBzY2FsaW5nIGhhcyBiZWVuIGRvbmUuDQoNClNlbGVjdGluZyBhIGRheSBvbiB0aGUgbGVmdCBncmFwaCBzaG93cyB3aGVyZSB0aGUgZGVhdGhzIGZyb20gdGhhdCBkYXkgd291bGQgbGlrZWx5IG9jY3VyLiBUaGUgbG9uZyByaWdodCB0YWlsIG9mIHRoZSBtb2RlbCBkaXN0cmlidXRpb24gcHVzaGVzIHRoZSBwZWFrIG9mIHRoZSBzdGFja2VkIGhpc3RvZ3JhbSB0byB0aGUgcmlnaHQuDQoNCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTEwfQ0KIyBSZWFkIGluIGNhc2UgZGF0YSBhbmQgc2VsZWN0IE5TVw0KY2FzZXMgPSByZWFkLmNzdignY2FzZXNfZGFpbHlfc3RhdGVfTkVULmNzdicpDQpjYXNlcyA8LSBzdWJzZXQoY2FzZXMsIHNlbGVjdD1jKMOvLi5EYXRlLE5TVykpDQojIENvbnZlcnQgZGF0ZSBmb3JtYXQgYW5kIHNlbGVjdCB0aW1lIHBlcmlvZCBvZiBpbnRlcmVzdA0KY2FzZXMgPC0gcmVuYW1lKGNhc2VzLCBEYXRlPcOvLi5EYXRlKQ0KY2FzZXMkRGF0ZSA8LSBhcy5EYXRlKGNhc2VzJERhdGUsICclZC8lbS8leScpDQpjYXNlcyA8LSBjYXNlcyAlPiUgZmlsdGVyKERhdGU+JzIwMjEtMDYtMTUnICYgRGF0ZTwnMjAyMS0xMC0zMScpDQojIENvbnZlcnQgY291bnQgZm9ybWF0DQpjYXNlcyROU1cgPC0gYXMubnVtZXJpYyhnc3ViKCIsIiwgIiIsIGNhc2VzJE5TVykpDQoNCiMgTWFrZSBkYXRhIGZyYW1lIHdpdGggZGlzdHJpYnV0aW9ucw0KRFMgPSBkYXRhLmZyYW1lKHBvcz1pbnRlZ2VyKCksZGF5PWNoYXJhY3RlcigpKQ0KZm9yKGkgaW4gMTpsZW5ndGgoY2FzZXMkTlNXKSkNCnsNCiAgIyBnZW5lcmF0ZSBkaXN0cmlidXRpb24gZm9yIHRoYXQgZGF5cyBwb3NpdGl2ZSB0ZXN0cyArIG51bSBkYXlzIHNpbmNlIHN0YXJ0DQogIHBvc190ZXN0cyA9IGNhc2VzJE5TV1tpXQ0KICBpZiAocG9zX3Rlc3RzID4gMCkgew0KICAgIGRfZGlzdCA8LSByc24obj1wb3NfdGVzdHMsIHhpPWRlbCwgb21lZ2E9b20sIGFscGhhPWFsKQ0KICAgIGRfZGlzdCA8LSBkX2Rpc3RbZF9kaXN0Pj0wXStpDQogICAgZF9kaXN0IDwtIGRhdGEuZnJhbWUoZF9kaXN0LCBpKSAlPiUgcmVuYW1lKGRlYXRocz1kX2Rpc3QsZGF5PWkpDQogICAgRFMgPC0gcmJpbmQoRFMsIGRfZGlzdCkNCiAgfQ0KfQ0KDQojIENvbnZlcnQgZGF5IG9mIGluZmVjdGlvbiBhbmQgZGVhdGggdG8gZGF0ZSAtIEFsdGFpciBuZWVkcyB0aW1lc3RhbXANCiMgU2V0IHN0YXJ0IGRhdGUNCnN0YXJ0IDwtIGFzLlBPU0lYY3QoYXMuRGF0ZSgnMjAyMS0wNi0xNScpKQ0KIyBEZWZpbmUgb25lIGRheQ0KcGx1czEgPC0gYXMuUE9TSVhjdChhcy5EYXRlKCcyMDIxLTA2LTE2JykpDQpvbmVfZGF5IDwtIHBsdXMxLXN0YXJ0DQojIEFkZCB0byBEUyBmcmFtZQ0KRFMkRGF0ZSA8LSBzdGFydCArIERTJGRheSpvbmVfZGF5DQpEUyRkX2RhdGUgPC0gc3RhcnQgKyBmbG9vcihEUyRkZWF0aHMpKm9uZV9kYXkNCkRTIDwtIHN1YnNldChEUywgc2VsZWN0ID0gYyhkZWF0aHMsIERhdGUpKQ0KIyBTZXQgbnVtYmVyIG9mIGJpbnMgZm9yIGhpc3RvZ3JhbXMNCm51bV9iaW5zIDwtIGNlaWxpbmcobWF4KERTJGRlYXRocyktbWluKERTJGRlYXRocykpDQojIEV4dHJhIGVudHJ5IC0gaGFjayB0byBleHRlbmQgeC1heGlzIGZvciBjYXNlIGhpc3RvZ3JhbQ0KIyBBbHRhaXIgZG9lc24ndCBkbyB0aGlzIG5pY2VseSB3aXRoIGRhdGV0aW1lcw0KIyBOQiB0aGVyZSBhcmUgMTg3IGRheXMgZnJvbSAxNi82IC0gMjAvMTINCiNlbmQgPC0gYXMuUE9TSVhjdChhcy5EYXRlKCcyMDIxLTEyLTIwJykpDQojcm93IDwtIGRhdGEuZnJhbWUoMTc5LGVuZCkNCiNuYW1lcyhyb3cpIDwtIGMoImRlYXRocyIsICJEYXRlIikNCiNEUyA8LSByYmluZChEUywgcm93KQ0KDQojIyMgQnVpbGQgaW50ZXJhY3RpdmUgY2hhcnRzDQojIERlZmluZSBpbnRlcmFjdGl2ZSBwbG90IHNlbGVjdG9yDQpzZWxlY3RvciA8LSBhbHQkc2VsZWN0aW9uX3NpbmdsZShlbXB0eSA9ICJhbGwiLCBmaWVsZHMgPSBsaXN0KCJEYXRlIikpDQoNCiMgSGlzdG9ncmFtIG9mIGNhc2VzIHdpdGggbGluZSBhbmQgZGF0ZSBvZiBtYXggY2FzZXMNCmhfY2FzZSA8LSANCiAgYWx0JENoYXJ0KERTKSQNCiAgbWFya19iYXIoc2l6ZT0yKSQNCiAgZW5jb2RlKA0KICAgIHggPSBhbHQkWCgNCiAgICAgICJEYXRlOlQiLCANCiAgICAgIGF4aXM9YWx0JEF4aXMoDQogICAgICAgIHRpdGxlPSdEYXRlJywNCiAgICAgICAgZ3JpZD1GDQogICAgICApLA0KICAgICAgdGltZVVuaXQ9Im1vbnRoZGF0ZSINCiAgICApLA0KICAgIHkgPSAiY291bnQoRGF0ZSk6USIsDQogICAgY29sb3IgPSBhbHQkY29uZGl0aW9uKA0KICAgICAgc2VsZWN0b3IsDQogICAgICAiRGF0ZTpUIiwgDQogICAgICBsZWdlbmQ9TlVMTCwNCiAgICAgIHNjYWxlPWFsdCRTY2FsZShzY2hlbWU9YWx0JFNjaGVtZVBhcmFtcyhuYW1lPSd2aXJpZGlzJywgZXh0ZW50PWMoLTAuNSwxLjUpKSksDQogICAgICBhbHQkdmFsdWUoImxpZ2h0Z3JheSIpDQogICAgKQ0KICApJA0KICBwcm9wZXJ0aWVzKA0KICAgIHNlbGVjdGlvbiA9IHNlbGVjdG9yLA0KICAgIHRpdGxlPSdEYWlseSBjYXNlcyAtIE5TVycNCiAgKQ0KDQojIENyZWF0ZSBkYXRhZnJhbWUgd2l0aCBtb2RlIHZhbHVlIGZvciBsaW5lDQptX2Nhc2UgPC0gYXMuUE9TSVhjdChhcy5EYXRlKCcyMDIxLTA5LTExJykpDQojIFB1dCB2ZXJ0aWNhbCBsaW5lIHNob3dpbmcgbW9kZSBvbiBncmFwaA0KbF9jYXNlIDwtIA0KICBhbHQkQ2hhcnQoZGF0YS5mcmFtZShtX2Nhc2UpKSQNCiAgbWFya19ydWxlKA0KICAgIGNvbG9yPSdkYXJrZ3JleScsDQogICAgc3Ryb2tlRGFzaD1hcnJheSgxLDEpDQogICkkDQogIGVuY29kZSh4PSdtb250aGRhdGUobV9jYXNlKTpUJykNCg0KIyBDcmVhdGUgdGV4dCBkYXRhZnJhbWUNCnRkX2Nhc2UgPC0gJzExLVNlcCcNCnRfY2FzZSA8LSANCiAgYWx0JENoYXJ0KGRhdGEuZnJhbWUodGRfY2FzZSkpJA0KICBtYXJrX3RleHQoDQogICAgZHg9NTIsDQogICAgZHk9LTExMA0KICApJA0KICBlbmNvZGUodGV4dD0ndGRfY2FzZTpOJykkDQogIHByb3BlcnRpZXMod2lkdGggPSAzNTAsIGhlaWdodCA9IDIwMCkNCg0KIyBDb21iaW5lIGxheWVycw0KZF9jYXNlIDwtIGhfY2FzZSArIGxfY2FzZSArIHRfY2FzZQ0KDQoNCiMjIyBEaXN0cmlidXRpb24gcGxvdHMNCiMgT3ZlcmxhaWQgaGlzdG9ncmFtcw0KaF9vdmVyIDwtIA0KICBhbHQkQ2hhcnQoRFMpJA0KICBtYXJrX2FyZWEoaW50ZXJwb2xhdGU9InN0ZXAiKSQNCiAgZW5jb2RlKA0KICAgIHggPSBhbHQkWCgNCiAgICAgICJkZWF0aHMiLCANCiAgICAgIGJpbj1hbHQkQmluKG1heGJpbnM9bnVtX2JpbnMpLCANCiAgICAgIHNjYWxlID0gYWx0JFNjYWxlKGRvbWFpbiA9IGMoMCwgMTg3KSksDQogICAgICBheGlzPWFsdCRBeGlzKHRpdGxlPSdEYXlzIHNpbmNlIDE2dGggSnVuZSAyMDIxJykNCiAgICApLA0KICAgIHkgPSBhbHQkWSgNCiAgICAgICJjb3VudCgpOlEiLCANCiAgICAgIHN0YWNrPUYsIA0KICAgICAgc2NhbGUgPSBhbHQkU2NhbGUoZG9tYWluID0gYygwLCAxMDApKQ0KICAgICksDQogICAgY29sb3IgPSBhbHQkQ29sb3IoDQogICAgICAiRGF0ZTpUIiwgDQogICAgICBsZWdlbmQ9TlVMTA0KICAgICksDQogICAgb3BhY2l0eT1hbHQkY29uZGl0aW9uKA0KICAgICAgc2VsZWN0b3IsDQogICAgICBhbHQkdmFsdWUoMSksDQogICAgICBhbHQkdmFsdWUoMC4wNSkNCiAgICApDQogICkkDQogIGFkZF9zZWxlY3Rpb24oc2VsZWN0b3IpJA0KICBwcm9wZXJ0aWVzKA0KICAgIHNlbGVjdGlvbiA9IHNlbGVjdG9yLCANCiAgICB0aXRsZT0nRGF0ZSBvZiBkZWF0aCBoaXN0b2dyYW1zIC0gb3ZlcmxhaWQnDQogICkNCg0KIyBDcmVhdGUgZGF0YWZyYW1lIHdpdGggbW9kZSB2YWx1ZSBmb3IgbGluZQ0KbV9vdmVyIDwtIDk1DQpsX292ZXIgPC0gDQogIGFsdCRDaGFydChkYXRhLmZyYW1lKG1fb3ZlcikpJA0KICBtYXJrX3J1bGUoDQogICAgY29sb3I9J2RhcmtncmV5JywNCiAgICBzdHJva2VEYXNoPWFycmF5KDEsMSkNCiAgKSQNCiAgZW5jb2RlKHg9J21fb3ZlcjpRJykNCg0KIyBDcmVhdGUgdGV4dCBkYXRhZnJhbWUNCnRkX292ZXIgPC0gJzE5LVNlcCcNCnRfb3ZlciA8LSANCiAgYWx0JENoYXJ0KGRhdGEuZnJhbWUodGRfb3ZlcikpJA0KICBtYXJrX3RleHQoDQogICAgZHg9LTExLA0KICAgIGR5PS0xMTANCiAgICApJA0KICBlbmNvZGUodGV4dD0ndGRfb3ZlcjpOJykkDQogIHByb3BlcnRpZXMod2lkdGggPSAzNTAsIGhlaWdodCA9IDIwMCkNCg0KIyBDb21iaW5lIGxheWVycw0KZF9vdmVyIDwtIGhfb3ZlciArIGxfb3ZlciArIHRfb3Zlcg0KDQoNCiMgU3RhY2tlZCBoaXN0b2dyYW1zDQpoX3N0YWsgPC0gDQogIGFsdCRDaGFydChEUykkDQogIG1hcmtfYXJlYShpbnRlcnBvbGF0ZT0ic3RlcCIpJA0KICBlbmNvZGUoDQogICAgeCA9IGFsdCRYKA0KICAgICAgImRlYXRocyIsIA0KICAgICAgYmluPWFsdCRCaW4obWF4Ymlucz1udW1fYmlucyksIA0KICAgICAgc2NhbGUgPSBhbHQkU2NhbGUoZG9tYWluID0gYygwLCAxODcpKSwNCiAgICAgIGF4aXM9YWx0JEF4aXModGl0bGU9J0RheXMgc2luY2UgMTZ0aCBKdW5lIDIwMjEnKQ0KICAgICksDQogICAgeSA9IGFsdCRZKA0KICAgICAgImNvdW50KCkiLA0KICAgICAgc3RhY2s9VCwNCiAgICAgIHNjYWxlID0gYWx0JFNjYWxlKGRvbWFpbiA9IGMoMCwgMTI1MCkpDQogICAgKSwNCiAgICBjb2xvciA9IGFsdCRDb2xvcigNCiAgICAgICJEYXRlOlQiLCANCiAgICAgIGxlZ2VuZD1OVUxMDQogICAgKSwNCiAgICBvcGFjaXR5PWFsdCRjb25kaXRpb24oDQogICAgICBzZWxlY3RvciwNCiAgICAgIGFsdCR2YWx1ZSgxKSwNCiAgICAgIGFsdCR2YWx1ZSgwLjMpDQogICAgKQ0KICApJA0KICBhZGRfc2VsZWN0aW9uKHNlbGVjdG9yKSQNCiAgcHJvcGVydGllcygNCiAgICBzZWxlY3Rpb24gPSBzZWxlY3RvciwgDQogICAgdGl0bGU9J0RhdGUgb2YgZGVhdGggaGlzdG9ncmFtcyAtIHN0YWNrZWQnDQogICkNCg0KIyBDcmVhdGUgZGF0YWZyYW1lIHdpdGggbW9kZSB2YWx1ZSBmb3IgbGluZQ0KbV9zdGFrIDwtIDEwMA0KbF9zdGFrIDwtIA0KICBhbHQkQ2hhcnQoZGF0YS5mcmFtZShtX3N0YWspKSQNCiAgbWFya19ydWxlKA0KICAgIGNvbG9yPSdkYXJrZ3JleScsDQogICAgc3Ryb2tlRGFzaD1hcnJheSgxLDEpDQogICkkDQogIGVuY29kZSh4PSdtX3N0YWs6UScpDQoNCiMgQ3JlYXRlIHRleHQgZGF0YWZyYW1lDQp0ZF9zdGFrIDwtICcyNC1TZXAnDQp0X3N0YWsgPC0gDQogIGFsdCRDaGFydChkYXRhLmZyYW1lKHRkX3N0YWspKSQNCiAgbWFya190ZXh0KA0KICAgIGR4PS0yLA0KICAgIGR5PS0xMTANCiAgKSQNCiAgZW5jb2RlKHRleHQ9J3RkX3N0YWs6TicpJA0KICBwcm9wZXJ0aWVzKHdpZHRoID0gMzUwLCBoZWlnaHQgPSAyMDApDQoNCmRfc3RhayA8LSBoX3N0YWsgKyBsX3N0YWsgKyB0X3N0YWsNCg0KIyBDb21iaW5lIGFsbCBncmFwaHMNCmZpbmFsIDwtIGRfY2FzZSB8IChkX292ZXIgJiBkX3N0YWspDQpmaW5hbCRjb25maWd1cmVfdGl0bGUoYW5jaG9yPSdzdGFydCcpDQoNCmBgYA0KV2hlbiB0aGUgZGlzdHJpYnV0aW9ucyBhcmUgb3ZlcmxhaWQgdGhlIGhpZ2hlc3QgcG9pbnQgaXMgYXQgMTl0aCBTZXB0ZW1iZXIgLSBhbiA4IGRheSBsYWcuIFdoZW4gdGhleSBzdGFja2VkIHRoZSBwZWFrIGlzIHRoZSAyNHRoIFNlcHRlbWJlciAtIGEgMTMgZGF5IGxhZy4gQ2xlYXJseSB0aGUgbW9kZWwgZGlzdHJpYnV0aW9uIGhlcmUgaXMgbm90IHF1aXRlIHJpZ2h0LCBidXQgaXQgc2VydmVzIHRvIGRlbW9uc3RyYXRlIGhvdyBhbGwgdGhlIGRpc3RyaWJ1dGlvbnMgcmVsYXRlIHRvIGVhY2ggb3RoZXIuIEl0IGlzIGFsc28gY2xlYXJlciBub3cgdGhhdCB1c2luZyB0aGUgbWVkaWFuIGFzIHRoZSBsYWcgd2lsbCBwcm9kdWNlIGEgbGVzcyBhY2N1cmF0ZSBlc3RpbWF0aW9uIG9mIHRoZSBkZWF0aCByYXRlIHdoZW4gbG9va2luZyBhdCB0aGUgZGVhdGhzIHJlcG9ydGVkIGVhY2ggZGF5LiBJbiBwcmFjdGljZSBpdCB3aWxsIHVuZGVyLWVzdGltYXRlIGFzIGNhc2VzIGluY3JlYXNlIGFuZCBvdmVyLWVzdGltYXRlIGFzIGNhc2VzIGRlY3JlYXNlLiBBbGlnbmluZyB0aGUgcGVha3Mgb2YgdGhlIGNhc2UgbnVtYmVycyBhbmQgdGhlIGRhaWx5IGRlYXRocyB3aWxsIGdpdmUgdGhlIGJlc3QgYXBwcm94aW1hdGlvbi4NCg0KPGJyPjxicj48YnI+DQoNCiMjIyMgUmVmZXJlbmNlcw0KMS4gQ09WSUQtMTkgQ2xpbmljYWwgSW5mb3JtYXRpb24gTmV0d29yayAoQ08tQ0lOKSwgPGk+VGltZSBmcm9tIHN5bXB0b20gb25zZXQgdW50aWwgZGVhdGggaW4gVUsgaG9zcGl0YWxpc2VkIHBhdGllbnRzPC9pPiwgNyBPY3RvYmVyIDIwMjAsIDxodHRwczovL3d3dy5nb3YudWsvZ292ZXJubWVudC9wdWJsaWNhdGlvbnMvY28tY2luLWNvdmlkLTE5LXRpbWUtZnJvbS1zeW1wdG9tLW9uc2V0LXVudGlsLWRlYXRoLWluLXVrLWhvc3BpdGFsaXNlZC1wYXRpZW50cy03LW9jdG9iZXItMjAyMD4NCg0KMi4gTWlrZSBIb25leSAyMDIxLCBhY2Nlc3NlZCAyMyBEZWNlbWJlciAyMDIxLCA8aHR0cHM6Ly9naXRodWIuY29tL01pa2UtSG9uZXkvY292aWQtMTktaG9zcGl0YWxpc2F0aW9ucz4NCg0KMy4gTlNXIE1pbmlzdHJ5IG9mIEhlYWx0aCAyMDIxLCBhY2Nlc3NlZCAyMyBEZWNlbWJlciAyMDIxLCA8aHR0cHM6Ly93d3cuaGVhbHRoLm5zdy5nb3YuYXUvSW5mZWN0aW91cy9jb3ZpZC0xOS9QYWdlcy93ZWVrbHktcmVwb3J0cy5hc3B4Pg0KDQpDYXNlIGRhdGEgZnJvbSA8YSBocmVmPSJodHRwczovL2luZm9ncmFtLmNvbS8xcHkybGpuNmpsNnEzMmIzbHIxbW54bjczZ2N5MDZxbmpsMj9saXZlIj5jb3ZpZDE5ZGF0YS5jb20uYXU8L2E+